#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/system.h>

#define _L_FLAG(tty,f)  ((tty)->termios.c_lflag & f)
#define _I_FLAG(tty,f)  ((tty)->termios.c_iflag & f)
#define _O_FLAG(tty,f)  ((tty)->termios.c_oflag & f)

#define L_CANON(tty)    _L_FLAG((tty),ICANON)
#define L_ISIG(tty)     _L_FLAG((tty),ISIG)
#define L_ECHO(tty)     _L_FLAG((tty),ECHO)
#define L_ECHOE(tty)    _L_FLAG((tty),ECHOE)
#define L_ECHOK(tty)    _L_FLAG((tty),ECHOK)
#define L_ECHOCTL(tty)  _L_FLAG((tty),ECHOCTL)
#define L_ECHOKE(tty)   _L_FLAG((tty),ECHOKE)

#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
#define I_NLCR(tty) _I_FLAG((tty),INLCR)
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)

#define O_POST(tty) _O_FLAG((tty),OPOST)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
#define O_NLRET(tty)    _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)

struct tty_struct tty_table[] = {
    {
        {ICRNL,         // 将输入的CR转换为NL
        OPOST|ONLCR,    // 将输出的NL转换为CRNL
        0,              // 控制模式初始化为0
        ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,    // 本地模式标志
        0,
        INIT_C_CC},     // 控制字符数组
        0,
        0,
        con_write,
        {0, 0, 0, 0, ""},
        {0, 0, 0, 0, ""},
        {0, 0, 0, 0, ""}
    }
};

struct tty_queue *table_list[] = {
    &tty_table[0].read_q, &tty_table[0].write_q,
};

void tty_init(void)
{
    con_init();
}

void copy_to_cooked(struct tty_struct *tty)
{
    signed char c;
    while (!EMPTY(tty->read_q)) {
        GETCH(tty->read_q, c);
        if (L_ECHO(tty)) {
            if (c == 13) {
                PUTCH(10, tty->write_q);
                PUTCH(13, tty->write_q);
            } else if (c < 32) {
                if (L_ECHOCTL(tty)) {
                    PUTCH('^', tty->write_q);
                    PUTCH(c + 64, tty->write_q);
                }
            } else
                PUTCH(c, tty->write_q);
            tty->write(tty);
        }
    }
}

int tty_write(unsigned channel, char *buf, int nr)
{
    struct tty_struct *tty;
    char c, *b = buf;

    if (channel > 0 || nr < 0)
        return -1;
    tty = channel + tty_table;

    while (nr > 0) {
        if (current->signal)
            break;
        while (nr > 0 && !FULL(tty->write_q)) {
            c = *b;
            b++; nr--;
            PUTCH(c, tty->write_q);
        }
        tty->write(tty);
        if (nr > 0)
            schedule();
    }
    return (b - buf);
}

void do_tty_interrupt(int tty)
{
    copy_to_cooked(tty_table + tty);
}
